#include "HomeDirectoryTree.h"
#include "HomeDirectoryWidget.h"
#include "HomeDirectoryFileEdit.h"

#include <QtWidgets>


HomeDirectoryTree::HomeDirectoryTree(QFileSystemModel & filesystem_model, const QStringList & hide_filenames)
    : _fs_model(filesystem_model)
{
    _sort_proxy = new HomeDirectorySortProxy(hide_filenames);  // создание класса-сортировки

    _sort_proxy->setSourceModel(&_fs_model);  // установка модели сортировки - модели файловой системы
    setModel(_sort_proxy);  // установка модели дерева - модели сортировки

    setColumnHidden(1, true);
    setColumnHidden(2, true);
    setColumnHidden(3, true);
    setAnimated(true);
    setHeaderHidden(true);
    setUniformRowHeights(true);

    setDragEnabled(true);
    setAcceptDrops(true);
    setDropIndicatorShown(true);
    setDragDropMode(QAbstractItemView::DragDrop);
    setDragDropOverwriteMode(false);

    setEditTriggers(QAbstractItemView::NoEditTriggers);

    _fs_model.setFilter(QDir::AllEntries | QDir::Hidden | QDir::NoDotAndDotDot);
    _fs_model.setReadOnly(false);

    /// \todo Это зачем?
    // connect(&_fs_model, &QFileSystemModel::fileRenamed, this, &HomeDirectoryTree::onFileRenamed);
}

HomeDirectoryTree::~HomeDirectoryTree()
{
    // setModel(nullptr); // потенциальная ошибка + зачем?

    delete _sort_proxy;
}

QFileInfo HomeDirectoryTree::getFileInfo(const QModelIndex &index) const
{
    return _fs_model.fileInfo(_sort_proxy->mapToSource(index));
}

QIcon HomeDirectoryTree::getFileIcon(const QString & file_path) const
{
    const QModelIndex &index = _fs_model.index(file_path);
    return _fs_model.fileIcon(index);
}

bool HomeDirectoryTree::isValidName(const QString & name) 
{
    std::string bad_chars = "!@%^\"'*~|/\\";

    std::string item_name = name.toStdString();

    if (!item_name.length())
        return false;
    for (size_t i = 0; i < item_name.length(); ++i) {
        for (size_t j = 0; j < bad_chars.length(); ++j) {
            if (item_name[i] == bad_chars[j])
                return false;
        }
    }

    return true;
}

bool HomeDirectoryTree::setSelectorPosition(const QString & path)
{
    if (!selectedIndexes().empty()) {
        const QModelIndex view_index = selectedIndexes().back();

        if (view_index.isValid())
            selectionModel()->select(view_index, QItemSelectionModel::Clear | QItemSelectionModel::Deselect);
    }

    QModelIndex new_index = _sort_proxy->mapFromSource(_fs_model.index(path));

    if (!new_index.isValid())
        return false;

    selectionModel()->select(new_index, QItemSelectionModel::SelectCurrent);
    scrollTo(new_index);

    return true;
}

// void HomeDirectoryTree::setSearchNameToSortProxy(const QString & search_name) 
// { 
//     _sort_proxy->setSearchName(search_name);
//     if (_choice == FileEditChoice::Find)
//         _sort_proxy->doInvalidateFilter();
// }

void HomeDirectoryTree::resetWorkDirectory()
{
    setRootIndex(_sort_proxy->mapFromSource(_fs_model.index(QDir::currentPath())));
}

bool HomeDirectoryTree::event(QEvent *event)
{
    if (event->type() == QEvent::ToolTip) {
        QHelpEvent *e = static_cast<QHelpEvent *>(event);

        const QModelIndex view_index = indexAt(e->pos());

        if (!view_index.isValid())
            return true;

        QModelIndex fs_index = _sort_proxy->mapToSource(view_index);

        qint64 size = _fs_model.size(fs_index);
        QString size_str;

        if (size > 100000000)
            size_str = QString(tr("%1 MB")).arg(size / 1000000);
        else if (size > 100000)
            size_str = QString(tr("%1 KB")).arg(size / 1000);
        else if (size == 0)
            size_str = "";
        else
            size_str = QString(tr("%1 B")).arg(size);

        QString text = "<i>file:</i> <b>" + _fs_model.fileName(fs_index) + "</b><br>" +
                       tr("<i>type:</i> ") + _fs_model.type(fs_index) + "<br>" +
                       ((size == 0) ? "" : (tr("<i>size:</i> ") + size_str + "<br>")) +
                       tr("<i>modified:</i> ") + _fs_model.lastModified(fs_index).toString("yyyy-MM-dd ddd HH:mm t");

        if (!text.isEmpty()) {
            QToolTip::showText(e->globalPos(), text);
        }
        else {
            QToolTip::hideText();
            event->ignore();
        }

        return true;
    }

    return QTreeView::event(event);
}

QString HomeDirectoryTree::addFileToDirectory(const QString & file_name)
{
    bool root = selectedIndexes().empty();

    QString path;

    if (!isValidName(file_name)) {
        QMessageBox::critical(this, tr("SIMODO"), tr("'%1' is invalid name of file").arg(file_name), QMessageBox::Ok);
        return {};
    }

    if (root) {
        path = QDir::current().canonicalPath();
    }
    else {
        const QModelIndex view_index = selectedIndexes().back();
        if (!view_index.isValid())
            return {};

        const QModelIndex fs_index = _sort_proxy->mapToSource(view_index);
        if (!fs_index.isValid())
            return {};

        QFileInfo fi = _fs_model.fileInfo(fs_index);
        if (fi.fileName().isEmpty() || !fi.isDir())
            return {};

        path = fi.filePath();
    }

    QString name = path + "/" + file_name;
    if (name.isEmpty())
        return {};

    {
        QFile file(name);
        if (!file.open(QIODevice::Text | QIODevice::WriteOnly))
            return {};
    }
    
    return name;
}

bool HomeDirectoryTree::addDirectoryToDirectory(const QString & directory_name)
{
    bool root = selectedIndexes().empty();

    QModelIndex view_index;
    QModelIndex fs_index;

    if (!isValidName(directory_name)) {
        QMessageBox::critical(this, tr("SIMODO"), tr("'%1' is invalid name of directory").arg(directory_name), QMessageBox::Ok);
        return false;
    }

    if (root) {
        fs_index = _fs_model.index(QDir::current().canonicalPath());
        if (!fs_index.isValid())
            return false;

        view_index = _sort_proxy->mapFromSource(fs_index);
        if (!view_index.isValid())
            return false;
    }
    else {
        view_index = selectedIndexes().back();
        if (!view_index.isValid())
            return false;

        fs_index = _sort_proxy->mapToSource(view_index);
        if (!fs_index.isValid())
            return false;
    }

    QString name = directory_name;
    if (name.isEmpty())
        return false;

    QModelIndex fs_new_dir_index = _fs_model.mkdir(fs_index,name);

    if (fs_new_dir_index.isValid())
    {
        QModelIndex view_new_dir_index = _sort_proxy->mapFromSource(fs_new_dir_index);
        if (view_new_dir_index.isValid())
        {
            selectionModel()->select(view_index,QItemSelectionModel::Clear|QItemSelectionModel::Deselect);
            selectionModel()->select(view_new_dir_index,QItemSelectionModel::SelectCurrent);
            scrollTo(view_new_dir_index);
            return true;
        }
    }

    return false;
}

QString HomeDirectoryTree::addFileToHomeDirectory(const QString & file_name)
{
    if (!isValidName(file_name)) {
        QMessageBox::critical(this, tr("SIMODO"), tr("'%1' is invalid name of file").arg(file_name), QMessageBox::Ok);
        return {};
    }

    QString path = QDir::current().canonicalPath();
    QString name = path + "/" + file_name;
    if (name.isEmpty())
        return {};

    {
        QFile file(name);
        if (!file.open(QIODevice::Text | QIODevice::WriteOnly))
            return {};
    }

    return name;
}

bool HomeDirectoryTree::addDirectoryToHomeDirectory(const QString & directory_name)
{
    if (!isValidName(directory_name)) {
        QMessageBox::critical(this, tr("SIMODO"), tr("'%1' is invalid name of directory").arg(directory_name), QMessageBox::Ok);
        return false;
    }

    QModelIndex fs_index = _fs_model.index(QDir::current().canonicalPath());
    if (!fs_index.isValid())
        return false;

    QModelIndex view_index = _sort_proxy->mapFromSource(fs_index);
    if (!view_index.isValid())
        return false;

    QString name = directory_name;
    if (name.isEmpty())
        return false;

    QModelIndex fs_new_dir_index = _fs_model.mkdir(fs_index, name);

    if (fs_new_dir_index.isValid())
    {
        QModelIndex view_new_dir_index = _sort_proxy->mapFromSource(fs_new_dir_index);
        if (view_new_dir_index.isValid())
        {
            selectionModel()->select(view_index,QItemSelectionModel::Clear|QItemSelectionModel::Deselect);
            selectionModel()->select(view_new_dir_index,QItemSelectionModel::SelectCurrent);
            scrollTo(view_new_dir_index);
            return true;
        }
    }

    return false;
}

bool HomeDirectoryTree::isFileSelected()
{
    return !selectedIndexes().empty();
}

QString HomeDirectoryTree::selectedPath() 
{
    return getFileInfo(selectedIndexes().front()).filePath();
}

void HomeDirectoryTree::editCurrent()
{
    edit(selectedIndexes().back());
}

void HomeDirectoryTree::expandCurrent()
{
    expand(selectedIndexes().back());
}

// void HomeDirectoryTree::onFileRenamed(const QString &/*path*/, const QString &/*oldName*/, const QString &/*newName*/)
// {
//     /// \todo Нужно добавить возможность переименовывать файлы через shell::Access_interface
//     if (MdiChild *existing = _main_window->findMdiChildCanonicalPath(path + "/" + oldName);
//         existing != nullptr)
//     {
//         existing->resetCurrentFileName(path + "/" +newName);
//     }
// }

void HomeDirectoryTree::onDuplicateFile()
{
    if (selectedIndexes().empty())
        return;

    const QModelIndex view_index = selectedIndexes().back();

    if (!view_index.isValid())
        return;

    const QModelIndex fs_index = _sort_proxy->mapToSource(view_index);

    if (!fs_index.isValid())
        return;

    QFileInfo fi = _fs_model.fileInfo(fs_index);

    if (fi.fileName().isEmpty() || !fi.isFile())
        return;

    int copy_count = 1;
    const int copy_limit = 100;
    QString fileName;

    for (; copy_count < copy_limit; ++copy_count) {
        fileName = fi.canonicalPath() + "/" +
                   fi.completeBaseName() + tr("~%1").arg(copy_count) + "." +
                   fi.suffix();

        if (!QFile::exists(fileName))
            break;
    }

    if (copy_count < copy_limit) {
        if (!QFile::copy(fi.filePath(), fileName)) {
            QMessageBox::critical(this, tr("SIMODO"),
                                  tr("Failed to copy file '%1'").arg(fileName),
                                  QMessageBox::Ok);
        }
    }
}

void HomeDirectoryTree::onDelete()
{
    if (selectedIndexes().empty())
        return;

    const QModelIndex view_index = selectedIndexes().back();

    if (!view_index.isValid())
        return;

    const QModelIndex fs_index = _sort_proxy->mapToSource(view_index);

    if (!fs_index.isValid())
        return;

    QFileInfo fi = _fs_model.fileInfo(fs_index);

    if (fi.fileName().isEmpty())
        return;

    QString item_type = fi.isDir() ? "Directory" : "File";
    QString item_name = fi.fileName();

    if (QMessageBox::Yes == QMessageBox::warning(this, tr("SIMODO"),
                                                 tr("%1 '%2' will be completely deleted.\nAre you sure?").arg(item_type).arg(item_name),
                                                 QMessageBox::Yes | QMessageBox::No))
    {
        bool ok = false;

        if (fi.isDir())
            ok = _fs_model.rmdir(fs_index);
        else if (fi.isFile())
            ok = _fs_model.remove(fs_index);

        if (!ok) {
            QMessageBox::critical(this, tr("SIMODO"),
                                  tr("Failed to delete '%1'").arg(item_name),
                                  QMessageBox::Ok);
            return;
        }
    }
}

